Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | const DEFAULT_ALLOWED_HOSTS_DEV = '*'; function parseBoolEnv(key: string, defaultValue: boolean): boolean { const raw = process.env[key]; if (raw == null) return defaultValue; const v = String(raw).trim().toLowerCase(); return v === '1' || v === 'true' || v === 'yes' || v === 'y' || v === 'on'; } export function requireAuthEnabled(): boolean { return parseBoolEnv('STREAMCORE_PROXY_REQUIRE_AUTH', process.env.NODE_ENV !== 'development'); } export function allowPrivateEnabled(): boolean { return parseBoolEnv('STREAMCORE_PROXY_ALLOW_PRIVATE', process.env.NODE_ENV === 'development'); } export function getAllowedHosts(): string[] { // Default to allowing all hosts since this is a streaming platform // that needs to proxy content from various CDNs and sources const raw = process.env.STREAMCORE_PROXY_ALLOWED_HOSTS || '*'; const list = String(raw) .split(',') .map((s) => s.trim()) .filter(Boolean); return list; } export function hasAuth(req: Request): boolean { const auth = req.headers.get('authorization') || ''; if (auth.toLowerCase().startsWith('bearer ')) return true; const cookie = req.headers.get('cookie') || ''; // Simple cookie lookup to avoid deps return cookie.split(';').some((part) => part.trim().startsWith('iptv_auth_token=')); } function isIpV4(hostname: string): boolean { return /^\d{1,3}(\.\d{1,3}){3}$/.test(hostname); } function ipV4ToBytes(ip: string): number[] | null { const parts = ip.split('.').map((p) => Number(p)); if (parts.length !== 4) return null; if (parts.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) return null; return parts; } function isPrivateIpV4(ip: string): boolean { const b = ipV4ToBytes(ip); if (!b) return false; const [a, c] = b; // 10.0.0.0/8 if (a === 10) return true; // 127.0.0.0/8 (loopback) if (a === 127) return true; // 169.254.0.0/16 (link-local) if (a === 169 && c === 254) return true; // 172.16.0.0/12 if (a === 172 && c >= 16 && c <= 31) return true; // 192.168.0.0/16 if (a === 192 && c === 168) return true; // 0.0.0.0/8, 100.64.0.0/10 (CGNAT) if (a === 0) return true; if (a === 100 && c >= 64 && c <= 127) return true; return false; } export function validateUpstreamUrl(urlParam: string): { ok: true; url: URL } | { ok: false; error: string; status: number } { let url: URL; try { url = new URL(urlParam); } catch { return { ok: false, error: 'Invalid url param', status: 400 }; } if (!/^https?:$/.test(url.protocol)) { return { ok: false, error: 'Only http/https URLs are allowed', status: 400 }; } if (url.username || url.password) { return { ok: false, error: 'Credentials in URL are not allowed', status: 400 }; } const hostname = url.hostname.toLowerCase(); if (hostname === 'localhost' || hostname.endsWith('.localhost')) { return { ok: false, error: 'Localhost is not allowed', status: 403 }; } if (isIpV4(hostname) && isPrivateIpV4(hostname) && !allowPrivateEnabled()) { return { ok: false, error: 'Private IP ranges are not allowed', status: 403 }; } const allowed = getAllowedHosts(); if (allowed.length === 0) { return { ok: false, error: 'Proxy is disabled (no allowlist configured)', status: 403 }; } if (!allowed.includes('*') && !allowed.includes(hostname)) { return { ok: false, error: `Host not allowed: ${hostname}`, status: 403 }; } return { ok: true, url }; } |